package com.zoro.dyformservice.support.web.converter;

import com.zoro.dyformservice.interfaces.CodeData;
import jakarta.annotation.Nonnull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class JsonSerializerScanner extends ClassPathBeanDefinitionScanner implements BeanDefinitionRegistryPostProcessor {

    private final Logger logger = LoggerFactory.getLogger(getClass());
    private final String basePackage;
    private final BeanDefinitionRegistry registry;

    public JsonSerializerScanner(String[] packages, BeanDefinitionRegistry registry) {
        super(registry);
        this.registry = registry;

        this.basePackage =
                StringUtils.collectionToCommaDelimitedString(Arrays.stream(packages).filter(StringUtils::hasText)
                        .collect(Collectors.toList()));

        // 设置扫描哪个注解修饰的类
        addIncludeFilter(new AnnotationTypeFilter(CustomJsonSerializerType.class));
        addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            Class<?> clazz;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            return CodeData.class.isAssignableFrom(clazz);
        });
        // 排除 package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
            String className = metadataReader.getClassMetadata().getClassName();
            return className.endsWith("package-info");
        });
    }


    @Override
    public void postProcessBeanDefinitionRegistry(@Nonnull BeanDefinitionRegistry registry) throws BeansException {
        this.scan(StringUtils.tokenizeToStringArray(this.basePackage,
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
    }

    @Override
    public void postProcessBeanFactory(@Nonnull ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }


    @Override
    @Nonnull
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 扫描结果
        if (basePackages.length == 0) {
            throw new IllegalArgumentException("At least one base package must be specified");
        }
        Set<Class<?>> codeDataClazzSet = new LinkedHashSet<>();
        Set<Class<?>> javaClassSet = new LinkedHashSet<>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                try {
                    Class<?> clazz = Class.forName(candidate.getBeanClassName());
                    CustomJsonSerializerType typeHandler = clazz.getDeclaredAnnotation(CustomJsonSerializerType.class);
                    if (typeHandler != null) {
                        // 使用MybatisTypeHandler 注解的类型
                        javaClassSet.add(clazz);
                    } else {
                        // CodeData 的子类
                        codeDataClazzSet.add(clazz);
                    }
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        BeanDefinitionBuilder builder =
                BeanDefinitionBuilder.genericBeanDefinition(CustomJsonSerializerProvider.class);
        builder.addConstructorArgValue(javaClassSet);
        builder.addConstructorArgValue(codeDataClazzSet);
        String beanName = CustomJsonSerializerProvider.class.getName() + "#" + getClass().getSimpleName() + "#" + 0;
        logger.debug("found CustomJsonSerializer Class {}.", javaClassSet.size());
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

        return new HashSet<>();
    }


    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        AnnotationMetadata metadata = beanDefinition.getMetadata();
        String className = metadata.getClassName();
        Class<?> clazz;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        /*
        1. 使用了 CustomJsonSerializerType 注解的类
        2. CodaData 的实现类并且是一个枚举
         */
        return metadata.getAnnotationTypes().contains(CustomJsonSerializerType.class.getName())
                || (CodeData.class.isAssignableFrom(clazz) && clazz.isEnum());
    }
}
